
var fs = require("fs");
// var glob = require('glob');  //注释掉打包。否则 electron 包无法正常使用
var path = require('path');
var exec = require('child_process').exec;
// var exe_path = path.join('static', 'draco-fontre', '2017.08.21', 'draco_encoder.exe');
var extname_obj = '.obj';
var extname_drc = '.drc';
var extname_ftrm = '.ftrm';
var extname_json = '.json';
var extname_ftrn = '.ftrn';
var max_pack_size = 500 * 1024;
var drc_size_inverted_order = true; //pack small => big
var type_mesh_drco = 0x01;
var type_mesh_obj = 0x02;
var type_json = 0x03;

function fontreconv (){

}

fontreconv.prototype.confuseJsonFile = function (fullPath, onDone) {
    path.normalize(fullPath);
    let outFilePath = path.dirname(fullPath) + path.sep + path.basename(fullPath, extname_json) + extname_ftrn;

    let self = this;
    let states = fs.statSync(fullPath);
    if(states.isFile()) {
        let totalLength = 4 + 64 + 1 + states.size;
        let buffer = new Buffer(totalLength);
        let offset = 0;
        //4 magic
        buffer.write("FTRN", offset, 4, "ascii");
        offset += 4;
        //64 rand
        buffer.write(self.randCode(64), offset, 64, "ascii");
        offset += 64;
        //1 type
        buffer.writeUInt8(type_json, offset);
        offset += 1;
        //
        let key = buffer.slice(20, 40);
        let key_length = 20;
        let data = fs.readFileSync(fullPath);
        for (let i=0; i<data.length; i++) {
            let val = (data[i] & 0xff) ^ (key[i % key_length] & 0xff);
            buffer.writeUInt8(val, offset);
            offset += 1;
        }
        //
        fs.writeFileSync(outFilePath, buffer);
        if (onDone) {
            onDone();
        }
        console.log(fullPath + " conv ftrn: " + outFilePath);
    } else {
        console.warn("conv error : " + fullPath);
        if (onDone) {
            onDone();
        }
    }
}

fontreconv.prototype.BlobToJson = function (blob) {
    return JSON.parse(this.Utf8ArrayToStr(new Uint8Array(blob)))
}
fontreconv.prototype.Utf8ArrayToStr = function(array) {
    var out = ''
    var i = 0
    var len = array.length
    var c
    var char2
    var char3
    while (i < len) {
        c = array[i++]
        switch (c >> 4) {
            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
            // 0xxxxxxx
            out += String.fromCharCode(c)
            break
            case 12: case 13:
            // 110x xxxx   10xx xxxx
            char2 = array[i++]
            out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F))
            break
            case 14:
                // 1110 xxxx  10xx xxxx  10xx xxxx
                char2 = array[i++]
                char3 = array[i++]
                out += String.fromCharCode(((c & 0x0F) << 12) |
                    ((char2 & 0x3F) << 6) |
                    ((char3 & 0x3F) << 0))
                break
        }
    }
    return out
}
fontreconv.prototype.decodeFtrnFile = function (fullPath, onDone) {
    path.normalize(fullPath);
    let outFilePath = path.dirname(fullPath) + path.sep + path.basename(fullPath, extname_ftrn) + extname_json;
    let states = fs.statSync(fullPath);
    if(states.isFile()) {
        let blob = fs.readFileSync(fullPath);
        var key = new Uint8Array(blob.slice(20, 40));
        var key_length = key.length;
        var buffer = new Uint8Array(blob.slice(69));
        var buffer_length = buffer.length;
        for (var i=0; i<buffer_length; i++)
        {
            buffer[i] = (buffer[i] & 0xff) ^ (key[i % key_length] & 0xff);
        }
        blob = buffer.buffer.slice(0);
        //
        let str = JSON.stringify(this.BlobToJson(blob),  function (key, value) {
            console.log(key + ":" +typeof(value));
            return value;
        }, "\t");
        fs.writeFileSync(outFilePath, str);
        if (onDone) {
            onDone();
        }
        console.log(fullPath + " decodeFtrnFile json: " + outFilePath);
    } else {
        console.warn("conv error : " + fullPath);
        if (onDone) {
            onDone();
        }
    }
}
fontreconv.prototype.confuseJsonDir = function (dir, onDone) {
    path.normalize(dir);
    let self = this;
    let fileList = this.getFileList(dir, extname_json);
    if (fileList && fileList.length > 0) {
        let jsonFileCount = 0;
        fileList.forEach(file => {
            self.confuseJsonFile(file.path, function() {
            jsonFileCount += 1;
            if (jsonFileCount === fileList.length) {
                if (onDone) {
                    onDone();
                }
            }
        });
    })
    } else {
        if (onDone) {
            onDone();
        }
    }
}

fontreconv.prototype.convObjFile2Drc = function (fullPath, onDone) {
    path.normalize(fullPath);
    let outFilePath = path.dirname(fullPath) + path.sep + path.basename(fullPath, extname_obj) + extname_drc;
    let cmdStr = tools_path + ' -cl 0' + ' -i ' + fullPath + ' -o ' + outFilePath;

    console.log(cmdStr);
    exec(cmdStr, function (err,stdout,stderr) {
        if (err) {
            console.warn(stderr);
        } else {
            console.log(stdout);
        }
        if (onDone) {
            onDone();
        }
    });
}

fontreconv.prototype.convObjDir2Drc = function (dir, onDone) {
    path.normalize(dir);
    let self = this;
    let fileList = this.getFileList(dir, extname_obj);
    if (fileList && fileList.length > 0) {
        let dracFileCount = 0;
        fileList.forEach(file => {
            self.convObjFile2Drc(file.path, function() {
                dracFileCount += 1;
                if (dracFileCount === fileList.length) {
                    if (onDone) {
                        onDone();
                    }
                }
            });
        })
    } else {
        if (onDone) {
            onDone();
        }
    }
}

fontreconv.prototype.packDrcDir = function (dir, onDone) {
    path.normalize(dir);
    let fileList = this.getFileList(dir, extname_drc);
    fileList.sort((a, b) => {
        if(a.size > b.size) {
            return drc_size_inverted_order? 1 : -1;
        } else if (a.size < b.size) {
            return drc_size_inverted_order? -1 : 1;
        }
        return 0;
    });
    this.packDrcFileList(fileList, dir, function () {
        if (onDone) {
            onDone();
        }
    });
}

fontreconv.prototype.packDrcFileList = function (list, dir, onDone) {
    let self = this;
    let arr = dir.split(path.sep);
    let folder = arr.pop();
    let size = 0;
    let files = [];
    let count = 1;
    list.forEach(file => {
        if (size + file.size >= max_pack_size) {
            let tmp = [].concat(files);
            let fullPath = dir + path.sep + folder + "_0" + count + extname_ftrm;
            self.doPackDrcFiles(tmp, fullPath, function () {
                console.log("pack ftrm file Done: " + fullPath);
            });
            //clear
            count += 1;
            size = 0;
            files.splice(0, files.length);
        } 
        files.push(file);
        size += file.size;
    })
    let fullPath = dir + path.sep + folder + "_0" + count + extname_ftrm;
    self.doPackDrcFiles(files, fullPath, function () {
        if (onDone) {
            console.log("pack ftrm file Done: " + fullPath);
            onDone();
        }
    });
}

fontreconv.prototype.doPackDrcFiles = function (files, fullPath, onDone) {
    let self = this;
    let file_num = files.length;
    let file_header_length = 4 + 64 + 4;
    let file_info_length = (1 + 4 + 4 + 64) * file_num;
    //
    let byte_offset = 0;
    let buffer = new Buffer(file_header_length + file_info_length);
    buffer.fill(0);
    //4 magic code
    buffer.write(self.magicCode(), byte_offset, 4, "ascii");
    byte_offset += 4;
    //64 rand
    buffer.write(self.randCode(64), byte_offset, 64, "ascii");
    byte_offset += 64;
    //4 file num
    buffer.writeUInt32LE(file_num, byte_offset);
    byte_offset += 4;
    //file info
    let content_offset = file_header_length + file_info_length;
    let contentArr = [buffer];
    files.forEach(file => {
        //1 type
        buffer.writeUInt8(type_mesh_drco, byte_offset);
        byte_offset += 1;
        //4 offset
        buffer.writeUInt32LE(content_offset, byte_offset);
        byte_offset += 4;
        //4 length
        buffer.writeUInt32LE(file.size, byte_offset);
        byte_offset += 4;
        //64 name
        buffer.write(file.name, byte_offset, file.name.length, "ascii")
        byte_offset += 64;
        //content offset
        content_offset += file.size;

        //
        var data = fs.readFileSync(file.path);
        contentArr.push(data);

        console.log("read file:" + file.name);
    });
    console.log("read all file done!");
    //
    let content = Buffer.concat(contentArr);
    fs.writeFileSync(fullPath, content);

    if (onDone) {
        onDone();
    }
    console.log("write all file done!");
}

//4
fontreconv.prototype.magicCode = function () {
    return 'FTRM';
}

fontreconv.prototype.randCode = function (len) {
	let uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	let lowercase = 'abcdefghijklmnopqrstuvwxyz';
	let digit = '0123456789';
    let data = '';
    
    for (let i = 0; i < len ; i++) {
        // Math.seedrandom(new Date().toLocaleString() + data);
        let flag = Math.floor(Math.random() * 10000) % 3;
        if (flag === 0) {
            data += uppercase.split('')[Math.floor(Math.random() * 10000) % uppercase.length];
        } else if (flag === 1) {
            data += lowercase.split('')[Math.floor(Math.random() * 10000) % lowercase.length];
        } else if (flag === 2) {
            data += digit.split('')[Math.floor(Math.random() * 10000) % digit.length];
        } else {
            data += 'x';
        }
    }
    return data;
}

fontreconv.prototype.getFixedLengthStr = function (str) {
    let buffer = new Buffer(str, "ascii");
    return buffer;
}

fontreconv.prototype.getFileList = function (dir, extname) {
    let filesList = [];
    this.readFile(dir, extname, filesList);
    return filesList;
}

fontreconv.prototype.readFile = function (dir, extname, filesList) {
    let files = fs.readdirSync(dir);
    files.forEach(file => {
        let fullPath = dir + path.sep + file;
        let states = fs.statSync(fullPath);
        let re = new RegExp(/^\./);
        if(states.isFile() && !re.test(file)) {
            if (path.extname(file) === extname) {
                let obj = new Object();
                obj.size = states.size;
                obj.name = file;
                obj.path = fullPath;
                filesList.push(obj);
            }
        }
    });
}

module.exports = fontreconv;
